/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - initial API and implementation
 */
package de.ipk_gatersleben.bit.bi.edal.primary_data;

import java.io.IOException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.security.auth.kerberos.KerberosPrincipal;

import org.apache.http.HttpHost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.net.SMTPAppender;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import com.btr.proxy.search.ProxySearch;
import com.btr.proxy.search.ProxySearch.Strategy;
import com.btr.proxy.util.PlatformUtil;
import com.btr.proxy.util.PlatformUtil.Platform;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.security.auth.NTUserPrincipal;
import com.sun.security.auth.UnixPrincipal;

import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.DataCiteMDSConnector;
import de.ipk_gatersleben.bit.bi.edal.sample.login.SamplePrincipal;

/**
 * class to collect all parameters to start the eDAL system.
 * 
 * @author arendd
 */
@SuppressWarnings("restriction")
public final class EdalConfiguration {

	private static Logger logger = null;

	static {
		DOMConfigurator.configure(EdalConfiguration.class
				.getResource("log4j.xml"));
		EdalConfiguration.logger = Logger.getLogger("eDAL-API");
	}

	public static final String DATACITE_SEARCH_URL = "http://search.datacite.org/api/";

	private static final String DATACITE_MDS_URL = "https://mds.datacite.org/doi";

	public static final String DATACITE_TESTPREFIX = "10.5072";

	private boolean inTestMode = false;

	/**
	 * Connection timeout for HTTP connection to DataCite in milliseconds.
	 */
	public static final int DATACITE_CONNECTION_TIMEOUT = 10000;

	/**
	 * Connection read timeout for HTTP connection to DataCite in milliseconds.
	 */
	public static final int DATACITE_CONNECTION_READ_TIMEOUT = 10000;

	/**
	 * Connection timeout for SMTP connection.
	 */
	public static final int SMTP_CONNECTION_TIMEOUT = 5000;

	/**
	 * Default names to check the eMail server.
	 */
	private static final String[] MAIL_SERVER_NAMES = { "IMAP", "POP", "MAIL",
			"SMTP", "EXCHANGE" };

	public static InetSocketAddress guessProxySettings() {

		ProxySearch proxySearch = new ProxySearch();

		if (PlatformUtil.getCurrentPlattform() == Platform.WIN) {
			proxySearch.addStrategy(Strategy.IE);
			proxySearch.addStrategy(Strategy.FIREFOX);
			proxySearch.addStrategy(Strategy.JAVA);
			proxySearch.addStrategy(Strategy.BROWSER);
			proxySearch.addStrategy(Strategy.JAVA);
			proxySearch.addStrategy(Strategy.ENV_VAR);
			proxySearch.addStrategy(Strategy.OS_DEFAULT);
		} else if (PlatformUtil.getCurrentPlattform() == Platform.LINUX) {
			proxySearch.addStrategy(Strategy.GNOME);
			proxySearch.addStrategy(Strategy.KDE);
			proxySearch.addStrategy(Strategy.FIREFOX);
			proxySearch.addStrategy(Strategy.BROWSER);
			proxySearch.addStrategy(Strategy.JAVA);
			proxySearch.addStrategy(Strategy.ENV_VAR);
			proxySearch.addStrategy(Strategy.OS_DEFAULT);
		} else {
			proxySearch.addStrategy(Strategy.OS_DEFAULT);
		}

		/**
		 * Note: do not override the default ProxSelector
		 * ProxySelector.setDefault(proxySearch.getProxySelector());
		 * 
		 * If the ProxySelector return null, all SearchStrategies failed
		 */

		try {
			List<Proxy> proxyList = proxySearch.getProxySelector().select(
					new URI(DATACITE_MDS_URL));
			if (proxyList != null) {
				for (Iterator<Proxy> i = proxyList.iterator(); i.hasNext();) {
					Proxy proxy = (Proxy) i.next();
					if (proxy.type().equals(Type.HTTP)) {
						InetSocketAddress address = (InetSocketAddress) proxy
								.address();

						return address;
					}
				}
			}
		} catch (URISyntaxException | NullPointerException e) {
			EdalConfiguration.logger
					.info("Find no proxy settings automatically");
		}
		return null;
	}

	/**
	 * The eMail address to send messages of edal.
	 */
	private String edalEmailAddress = "noreply@ipk-gatersleben.de";

	private String dataCitePassword;

	private String dataCitePrefix;

	private String dataCiteUser;
	
	public static final Path DEFAULT_PATH = Paths.get(
			System.getProperty("user.home"), "edal");
	/**
	 * The mount {@link Path} of the eDAL system.
	 */
	private Path mountPath = DEFAULT_PATH;

	/**
	 * The data {@link Path} of the eDAL system.
	 */
	private Path dataPath = DEFAULT_PATH;

	public static final URL KEY_STORE_PATH = EdalConfiguration.class
			.getClassLoader().getResource(
					"de/ipk_gatersleben/bit/bi/edal/primary_data/keystore.jks");

	public static final String KEYSTORE_PASSWORD = "eDALkey";

	public static String guessSmtpSettings(String smtpLogin, String smtpPassword)
			throws EdalConfigurationException {

		for (String mailServerNames : MAIL_SERVER_NAMES) {
			Properties properties = new Properties();
			properties.put("mail.smtp.host", mailServerNames);
			properties.put("mail.smtp.connectiontimeout",
					SMTP_CONNECTION_TIMEOUT);
			properties.put("mail.smtp.timeout", SMTP_CONNECTION_TIMEOUT);
			// properties.put("mail.smtp.starttls.enable", "true");
			properties.put("mail.smtp.ssl.trust", mailServerNames);

			Session session = Session.getDefaultInstance(properties);

			try {
				if (!smtpLogin.isEmpty() && !smtpPassword.isEmpty()) {
					session.getTransport("smtp").connect(smtpLogin,
							smtpPassword);

				} else {
					session.getTransport("smtp").connect();
				}
				// Logger edalLogger = Logger
				// .getLogger("EDAL_SMTP_ERROR_APPENDER");
				//
				// SMTPAppender smtp = new SMTPAppender();
				//
				// smtp.setName("SMTP-APP");
				// smtp.setFrom(this.getEdalEmailAddress());
				// smtp.setTo(getErrorEmailAddress().getAddress());
				// smtp.setSubject("[eDAL ERROR]");
				// smtp.setSMTPHost(mailServerNames);
				// smtp.setSMTPUsername(this.getMailSmtpLogin());
				// smtp.setSMTPPassword(this.getMailSmtpPassword());
				// smtp.setThreshold(Level.FATAL);
				// smtp.setBufferSize(512);
				// smtp.setLayout(new SimpleLayout());
				// smtp.activateOptions();
				//
				// edalLogger.addAppender(smtp);
				// this.setErrorLogger(edalLogger);

				EdalConfiguration.logger.info("SMTP Connection Test: "
						+ mailServerNames + " : successful");
				return mailServerNames;
			} catch (MessagingException e) {
				EdalConfiguration.logger.warn("SMTP Connection Test: "
						+ mailServerNames + " -> failed : " + e.getMessage());
			}
		}
		throw new EdalConfigurationException(
				"unable to connect to eMail Server, check SMTP settings");
	}

	private boolean useSSL = true;
	private InternetAddress errorEmailAddress;
	private Logger errorLogger;
	/**
	 * The password for the H2 mount user
	 */
	private String h2Password;

	/**
	 * The name of H2 mount user
	 */
	private String h2UserName;
	/**
	 * Port for the HTTP server/listener
	 */
	private int httpPort;
	/**
	 * Optional proxy parameter to connect with the DataCite connector
	 */
	private String httpProxyHost;
	private int httpProxyPort;
	private String httpsProxyHost;
	private int httpsProxyPort;
	/**
	 * SMTP configuration to send approval eMails
	 */
	private String mailSmtpHost;

	private String mailSmtpLogin;

	private String mailSmtpPassword;

	private InternetAddress reviewerManaging;

	private InternetAddress reviewerScientific;

	private InternetAddress reviewerSubstitute;

	private InternetAddress rootUser;

	/**
	 * {@link List} of supported {@link Principal}s for the security system
	 */
	private List<Class<? extends Principal>> supportedPrincipals;

	private boolean useSystemProxies = false;
	/**
	 * The default supported principals by eDAL.
	 */
	public static final List<Class<? extends Principal>> DEFAULT_SUPPORTED_PRINCIPALS = new ArrayList<Class<? extends Principal>>(
			Arrays.asList(SamplePrincipal.class, NTUserPrincipal.class,
					UnixPrincipal.class, KerberosPrincipal.class));

	/**
	 * The default H2 user name
	 */
	public static final String DEFAULT_H2_USERNAME = "sa";

	/**
	 * The default H2 password
	 */
	public static final String DEFAULT_H2_PASSWORD = "";

	/**
	 * The default port for the HTTP listener
	 */
	public static final int DEFAULT_HTTP_PORT = 8085;

	/**
	 * @return the useSSLForHttpListener
	 */
	public boolean isUseSSLForHttpListener() {
		return useSSLForHttpListener;
	}

	/**
	 * @param useSSLForHttpListener
	 *            the useSSLForHttpListener to set
	 */
	public void setUseSSLForHttpListener(boolean useSSLForHttpListener,
			URL pathToKeyStore, String keystorePassword) {
		this.useSSLForHttpListener = useSSLForHttpListener;

		this.setCertificatePathForHttpListener(pathToKeyStore);

		this.setKeystorePasswordForHttpListener(keystorePassword);
	}

	private boolean useSSLForHttpListener = false;
	private URL certificatePathForHttpListener = null;
	private String keystorePasswordForHttpListener = "";

	/**
	 * default constructor set default values for parameter that has not
	 * specified explicitly by user <br>
	 * 
	 * @throws EdalConfigurationException
	 *             if unable to load reviewer rule files.
	 */
	private EdalConfiguration() throws EdalConfigurationException {

		this.setH2User(DEFAULT_H2_USERNAME);
		this.setH2Password(DEFAULT_H2_PASSWORD);
		this.setSupportedPrincipals(DEFAULT_SUPPORTED_PRINCIPALS);
		this.setHttpPort(DEFAULT_HTTP_PORT);
		this.setMailSmtpHost("");
		this.setMailSmtpLogin("");
		this.setMailSmtpPassword("");
	}

	public EdalConfiguration(String dataCiteUser, String dataCitePassword,
			String dataCitePrefix, InternetAddress scientificReviewer,
			InternetAddress substituteReviewer,
			InternetAddress managingReviewer, InternetAddress rootUser)
			throws EdalConfigurationException {

		this();
		this.setDataCiteUser(dataCiteUser);
		this.setDataCitePassword(dataCitePassword);
		this.setDataCitePrefix(dataCitePrefix);
		this.setReviewerScientific(scientificReviewer);
		this.setReviewerSubstitute(substituteReviewer);
		this.setReviewerManaging(managingReviewer);
		this.setRootUser(rootUser);
		this.setErrorEmailAddress(rootUser);
		this.validate();

	}

	public EdalConfiguration(String dataCiteUser, String dataCitePassword,
			String dataCitePrefix, InternetAddress scientificReviewer,
			InternetAddress substituteReviewer,
			InternetAddress managingReviewer, InternetAddress rootUser,
			String httpProxyHost, int httpProxyPort, String httpsProxyHost,
			int httpsProxyPort, String smtpHost, String smtpLogin,
			String smtpPassword) throws EdalConfigurationException {

		this();
		this.setUseSystemProxies(true);
		this.setHttpProxyHost(httpProxyHost);
		this.setHttpProxyPort(httpProxyPort);
		this.setHttpsProxyHost(httpsProxyHost);
		this.setHttpsProxyPort(httpsProxyPort);
		this.setDataCiteUser(dataCiteUser);
		this.setDataCitePassword(dataCitePassword);
		this.setDataCitePrefix(dataCitePrefix);
		this.setReviewerScientific(scientificReviewer);
		this.setReviewerSubstitute(substituteReviewer);
		this.setReviewerManaging(managingReviewer);
		this.setRootUser(rootUser);
		this.setErrorEmailAddress(rootUser);
		this.setMailSmtpHost(smtpHost);
		this.setMailSmtpLogin(smtpLogin);
		this.setMailSmtpPassword(smtpPassword);
		this.validate();

	}

	/**
	 * Add a supported Principal to the list of principals.
	 * 
	 * @param principal
	 *            the principal to add.
	 */
	public void addSupportedPrincipal(final Class<? extends Principal> principal) {
		this.supportedPrincipals.add(principal);
	}

	/**
	 * Getter for the DataCite password.
	 * 
	 * @return the DataCite password.
	 * @throws EdalConfigurationException
	 *             if no DataCite password is defined
	 */
	public String getDataCitePassword() throws EdalConfigurationException {
		if (this.dataCitePassword == null || this.dataCitePassword.isEmpty()) {
			throw new EdalConfigurationException("no DataCite password set!");
		}
		return this.dataCitePassword;
	}

	/**
	 * Getter for the DataCite prefix
	 * 
	 * @return the DataCite prefix
	 * @throws EdalConfigurationException
	 *             if no prefix is defined
	 */
	public String getDataCitePrefix() throws EdalConfigurationException {
		if (this.dataCitePrefix == null || this.dataCitePrefix.isEmpty()) {
			throw new EdalConfigurationException("no DataCite prefix set!");
		}
		return this.dataCitePrefix;
	}

	/**
	 * Getter for the DataCite user name.
	 * 
	 * @return the DataCite user name.
	 * @throws EdalConfigurationException
	 *             if no DataCite user name is defined
	 */
	public String getDataCiteUser() throws EdalConfigurationException {
		if (this.dataCiteUser == null || this.dataCiteUser.isEmpty()) {
			throw new EdalConfigurationException("no DataCite user name set!");
		}
		return this.dataCiteUser;
	}

	/**
	 * @return the data path.
	 */
	public Path getDataPath() {
		return this.dataPath;
	}

	/**
	 * Getter for the edal email address.
	 * 
	 * @return the email address for edal messages.
	 */
	public String getEdalEmailAddress() {
		return this.edalEmailAddress;
	}

	/**
	 * Getter for the eMail address to send error messages.
	 * 
	 * @return the errorEmail
	 * @throws EdalConfigurationException
	 */
	protected InternetAddress getErrorEmailAddress()
			throws EdalConfigurationException {
		if (this.errorEmailAddress == null) {
			throw new EdalConfigurationException("no error Email Address set!");
		}
		return errorEmailAddress;
	}

	/**
	 * @return the logger
	 */
	public Logger getErrorLogger() {
		return errorLogger;
	}

	/**
	 * Getter for the H2 password.
	 * 
	 * @return the h2Pass
	 * @throws EdalConfigurationException
	 *             if no H2 password is defined
	 */
	public String getH2Password() throws EdalConfigurationException {
		if (this.h2Password == null) {
			throw new EdalConfigurationException("no H2 pass set!");
		}
		return this.h2Password;
	}

	/**
	 * Getter for the H2 user name.
	 * 
	 * @return the h2UserName.
	 * @throws EdalConfigurationException
	 *             if no H2 user name is defined
	 */
	public String getH2UserName() throws EdalConfigurationException {

		if (this.h2UserName == null || this.h2UserName.isEmpty()) {
			throw new EdalConfigurationException("no H2 user set!");
		}
		return this.h2UserName;
	}

	/**
	 * Getter for the port of the HTTP server/listener.
	 * 
	 * @return the HTTP port.
	 * @throws EdalConfigurationException
	 *             if no HTTP port is set.
	 */
	public int getHttpPort() throws EdalConfigurationException {
		if (this.httpPort == 0) {
			throw new EdalConfigurationException("no http port set! ");
		}
		return this.httpPort;
	}

	/**
	 * Getter for the HTTP proxy host.
	 * 
	 * @return the HTTP proxy host.
	 * @throws EdalConfigurationException
	 *             if no HTTP proxy host is defined
	 */
	public String getHttpProxyHost() throws EdalConfigurationException {
		if (this.httpProxyHost == null || this.httpProxyHost.isEmpty()) {
			throw new EdalConfigurationException("no HTTP proxy host set!");
		}
		return this.httpProxyHost;
	}

	/**
	 * Getter for the HTTP proxy port.
	 * 
	 * @return the HTTP proxy port.
	 * @throws EdalConfigurationException
	 *             if no HTTP proxy port is defined
	 */
	public int getHttpProxyPort() throws EdalConfigurationException {
		if (this.httpProxyPort == 0) {
			throw new EdalConfigurationException("no HTTP proxy port set!");
		}
		return this.httpProxyPort;
	}

	/**
	 * Getter for the HTTPS proxy host.
	 * 
	 * @return the HTTPS proxy host.
	 * @throws EdalConfigurationException
	 *             if no HTTPS proxy host is defined
	 */
	public String getHttpsProxyHost() throws EdalConfigurationException {
		if (this.httpsProxyHost == null || this.httpsProxyHost.isEmpty()) {
			throw new EdalConfigurationException("no HTTPS proxy host set!");
		}
		return this.httpsProxyHost;
	}

	/**
	 * Getter for the HTTPS proxy port.
	 * 
	 * @return the HTTPS proxy port.
	 * @throws EdalConfigurationException
	 *             if no HTTPS proxy port is defined
	 */
	public int getHttpsProxyPort() throws EdalConfigurationException {
		if (this.httpsProxyPort == 0) {
			throw new EdalConfigurationException("no HTTPS port host set!");
		}
		return this.httpsProxyPort;
	}

	/**
	 * @return the logger
	 */
	public Logger getLogger() {
		return logger;
	}

	/**
	 * Getter for the mail SMTP host.
	 * 
	 * @return the mail SMTP host.
	 */
	public String getMailSmtpHost() {
		return this.mailSmtpHost;
	}

	/**
	 * Getter for the mail SMTP host.
	 * 
	 * @return the mail SMTP host.
	 */
	public String getMailSmtpLogin() {
		return this.mailSmtpLogin;
	}

	/**
	 * Getter for the mail SMTP password.
	 * 
	 * @return the mail SMTP password.
	 */
	public String getMailSmtpPassword() {
		return this.mailSmtpPassword;
	}

	/**
	 * Getter for the mount path of the eDAL system.
	 * 
	 * @return the mountPath
	 */
	public Path getMountPath() {
		return this.mountPath;
	}

	/**
	 * Getter for the eMail address of the managing reviewer.
	 * 
	 * @return the REVIEWER_MANAGING
	 * @throws EdalConfigurationException
	 *             if no emailAddress is defined or if it is invalid.
	 */
	public InternetAddress getReviewerManaging()
			throws EdalConfigurationException {
		if (this.reviewerManaging == null) {
			throw new EdalConfigurationException(
					"no email address for the managing reviewer set!");
		}
		try {
			this.reviewerManaging.validate();
		} catch (AddressException e) {
			throw new EdalConfigurationException(
					"invalid email address for managing reviewer: "
							+ e.getMessage());
		}
		return reviewerManaging;
	}

	/**
	 * Getter for the eMail address of the scientific reviewer.
	 * 
	 * @return the reviewerScientific
	 * @throws EdalConfigurationException
	 *             if no emailAddress is defined or if it is invalid.
	 */
	public InternetAddress getReviewerScientific()
			throws EdalConfigurationException {
		if (this.reviewerScientific == null) {
			throw new EdalConfigurationException(
					"no email address for the scientific reviewer set!");
		}
		try {
			this.reviewerScientific.validate();
		} catch (AddressException e) {
			throw new EdalConfigurationException(
					"invalid email address for scientific reviewer: "
							+ e.getMessage());
		}
		return reviewerScientific;
	}

	/**
	 * Getter for the eMail address of the substitute reviewer.
	 * 
	 * @return the reviewerSubstitute
	 * @throws EdalConfigurationException
	 *             if no emailAddress is defined or if it is invalid.
	 */
	public InternetAddress getReviewerSubstitute()
			throws EdalConfigurationException {
		if (this.reviewerSubstitute == null) {
			throw new EdalConfigurationException(
					"no email address for the substitute reviewer set!");
		}
		try {
			this.reviewerSubstitute.validate();
		} catch (AddressException e) {
			throw new EdalConfigurationException(
					"invalid email address for substitute reviewer: "
							+ e.getMessage());
		}
		return reviewerSubstitute;
	}

	/**
	 * Getter for the eMail address for the root user.
	 * 
	 * @return the rootUser
	 * @throws EdalConfigurationException
	 *             if no emailAddress is defined or if it is invalid.
	 */
	public InternetAddress getRootUser() throws EdalConfigurationException {

		if (this.rootUser == null) {
			throw new EdalConfigurationException(
					"no email address for the root user set!");
		}
		try {
			this.rootUser.validate();
		} catch (AddressException e) {
			throw new EdalConfigurationException(
					"invalid email address for root user: " + e.getMessage());
		}
		return rootUser;
	}

	/**
	 * Getter for the List of supported {@link Principal}s.
	 * 
	 * @return the List of supported {@link Principal}s
	 * @throws EdalConfigurationException
	 *             if no supported principals are defined !
	 */
	public List<Class<? extends Principal>> getSupportedPrincipals()
			throws EdalConfigurationException {
		if (this.supportedPrincipals.isEmpty()) {
			throw new EdalConfigurationException(
					"no supported principals defined!");
		}
		return this.supportedPrincipals;
	}

	/**
	 * @return the inTestModeE
	 */
	public boolean isInTestMode() {
		return this.inTestMode;
	}

	/**
	 * @return the useSSL
	 */
	public boolean isUseSSL() {
		return useSSL;
	}

	/**
	 * Check if proxies should be used.
	 * 
	 * @return true or false
	 */
	public boolean isUseSystemProxies() {
		return this.useSystemProxies;
	}

	/**
	 * Setter for the DataCite password.
	 * 
	 * @param dataCitePassword
	 */
	private void setDataCitePassword(final String dataCitePassword) {
		this.dataCitePassword = dataCitePassword;
	}

	/**
	 * Setter for the DataCite test prefix
	 * 
	 * @param dataCitePrefix
	 */
	private void setDataCitePrefix(final String dataCitePrefix) {
		this.dataCitePrefix = dataCitePrefix;
	}

	/**
	 * Setter for the DataCite user name.
	 * 
	 * @param dataCiteUser
	 */
	private void setDataCiteUser(final String dataCiteUser) {
		this.dataCiteUser = dataCiteUser;
	}

	/**
	 * Setter for the data path.
	 * 
	 * @param dataPath
	 *            the dataPath to set
	 */
	public void setDataPath(final Path dataPath) {
		this.dataPath = dataPath;
	}

	/**
	 * @param errorEmailAddress
	 *            the errorEmail to set
	 */
	protected void setErrorEmailAddress(InternetAddress errorEmailAddress) {
		this.errorEmailAddress = errorEmailAddress;
	}

	/**
	 * Setter for the error email logger.
	 * 
	 * @param errorLogger
	 *            the logger to set
	 */
	private void setErrorLogger(Logger errorLogger) {
		this.errorLogger = errorLogger;
	}

	/**
	 * Setter for the H2 password.
	 * 
	 * @param h2Password
	 *            the h2_PASS to set
	 */
	public void setH2Password(final String h2Password) {
		this.h2Password = h2Password;
	}

	/**
	 * Setter for the H2 user name.
	 * 
	 * @param h2UserName
	 *            the h2_USER to set
	 */
	public void setH2User(final String h2UserName) {
		this.h2UserName = h2UserName;
	}

	/**
	 * Setter for the port of the HTTP server/listener.
	 * 
	 * @param httpPort
	 *            the HTTP port to set.
	 */
	public void setHttpPort(final int httpPort) {
		this.httpPort = httpPort;
	}

	/**
	 * Setter for the HTTP proxy host.
	 * 
	 * @param httpProxyHost
	 *            the HTTP proxy host.
	 */
	public void setHttpProxyHost(final String httpProxyHost) {
		this.httpProxyHost = httpProxyHost;
	}

	/**
	 * Setter for the HTTP proxy port.
	 * 
	 * @param httpProxyPort
	 *            the HTTP proxy port.
	 */
	public void setHttpProxyPort(final int httpProxyPort) {
		this.httpProxyPort = httpProxyPort;
	}

	/**
	 * Setter for the HTTPS proxy host.
	 * 
	 * @param httpsProxyHost
	 *            the HTTP proxy host to set.
	 */
	public void setHttpsProxyHost(final String httpsProxyHost) {
		this.httpsProxyHost = httpsProxyHost;
	}

	/**
	 * Setter for the HTTPS proxy port.
	 * 
	 * @param httpsProxyPort
	 *            the HTTP proxy port to set.
	 */
	public void setHttpsProxyPort(final int httpsProxyPort) {
		this.httpsProxyPort = httpsProxyPort;
	}

	/**
	 * @param inTestMode
	 *            the iN_TEST_MODE to set
	 */
	private void setInTestMode(boolean inTestMode) {
		this.inTestMode = inTestMode;
	}

	/**
	 * Setter for the mail SMTP host.
	 * 
	 * @param mailSmtpHost
	 *            the mail SMTP host to set.
	 */
	public void setMailSmtpHost(final String mailSmtpHost) {
		this.mailSmtpHost = mailSmtpHost;
	}

	/**
	 * Setter for the mail SMTP login.
	 * 
	 * @param mailSmtpLogin
	 *            the mail SMTP login to set.
	 */
	public void setMailSmtpLogin(final String mailSmtpLogin) {
		this.mailSmtpLogin = mailSmtpLogin;
	}

	/**
	 * Setter for the SMTP password.
	 * 
	 * @param mailSmtpPassword
	 */
	public void setMailSmtpPassword(final String mailSmtpPassword) {
		this.mailSmtpPassword = mailSmtpPassword;
	}

	/**
	 * Setter for the mount path.
	 * 
	 * @param mountPath
	 *            the mount path to set
	 */
	public void setMountPath(final Path mountPath) {
		this.mountPath = mountPath;
	}

	/**
	 * Setter for the eMail address of the managing reviewer.
	 * 
	 * @param reviewerManaging
	 *            the eMail address of the managing reviewer to set
	 */
	private void setReviewerManaging(InternetAddress reviewerManaging) {
		this.reviewerManaging = reviewerManaging;
	}

	/**
	 * Setter for the eMail address of the scientific reviewer.
	 * 
	 * @param reviewerScientific
	 *            the eMail address of the scientific reviewer to set
	 */
	private void setReviewerScientific(InternetAddress reviewerScientific) {
		this.reviewerScientific = reviewerScientific;
	}

	/**
	 * Setter for the eMail address of the substitute reviewer.
	 * 
	 * @param reviewerSubstitute
	 *            the eMail address of the substitute reviewer to set
	 */
	private void setReviewerSubstitute(InternetAddress reviewerSubstitute) {
		this.reviewerSubstitute = reviewerSubstitute;
	}

	/**
	 * Setter for the eMail address of the root user.
	 * 
	 * @param rootUser
	 *            the rootUser to set
	 */
	private void setRootUser(InternetAddress rootUser) {
		this.rootUser = rootUser;
	}

	/**
	 * Setter for the supported {@link Principal}s.
	 * 
	 * @param supportedPrincipals
	 *            the supported {@link Principal}s to set.
	 */
	public void setSupportedPrincipals(
			final List<Class<? extends Principal>> supportedPrincipals) {
		this.supportedPrincipals = supportedPrincipals;
	}

	/**
	 * @param useSSL
	 *            the useSSL to set
	 */
	public void setUseSSL(boolean useSSL) {
		this.useSSL = useSSL;
	}

	/**
	 * Setter to activate the usage of proxies.
	 * 
	 * @param useSystemProxies
	 */
	public void setUseSystemProxies(final boolean useSystemProxies) {
		this.useSystemProxies = useSystemProxies;
	}

	/**
	 * Validate the {@link EdalConfiguration} object.
	 * 
	 * @return true if validation was successful.
	 * @throws EdalConfigurationException
	 *             if validation failed.
	 */
	private boolean validate() throws EdalConfigurationException {

		this.getMountPath();
		this.getH2UserName();
		this.getH2Password();
		this.getHttpPort();
		this.getSupportedPrincipals();

		this.getReviewerScientific();
		this.getReviewerSubstitute();
		this.getReviewerManaging();
		this.getRootUser();

		this.validateProxies();
		this.validateDataCiteConnection();
		this.validateDataCiteAuthentication();
		this.validateDateCiteSolrSearch();
		this.validateSmtpSettings();

		return true;
	}

	/**
	 * Validate the given DataCite user name and password.
	 * 
	 * @return true if the validation was successful, otherwise false.
	 * @throws EdalConfigurationException
	 *             if unable to validate the parameter.
	 */
	private boolean validateDataCiteAuthentication()
			throws EdalConfigurationException {

		if (!this.getDataCitePrefix().equals(
				EdalConfiguration.DATACITE_TESTPREFIX)) {

			this.getDataCiteUser();
			this.getDataCitePassword();
			this.getDataCitePrefix();

			DataCiteMDSConnector connector;
			try {
				connector = new DataCiteMDSConnector(this);
			} catch (EdalException e) {
				throw new EdalConfigurationException(
						"DataCite Authentification Test failed: unable to create DataCiteMDSConnector");
			}

			ClientResponse response = connector.getDOI(this.getDataCitePrefix()
					+ "/" + UUID.randomUUID().toString());

			if (response.getStatus() == 404 || response.getStatus() == 200) {
				EdalConfiguration.logger
						.info("DataCite Authentification Test: successful");
			}

			else if (response.getStatus() == 401) {
				throw new EdalConfigurationException(
						"DataCite Authentification failed: please check username and password");
			}

			else if (response.getStatus() == 500) {
				throw new EdalConfigurationException(
						"DataCite Authentification failed: please check prefix");
			} else {
				throw new EdalConfigurationException(
						"DataCite Authentification Test failed: "
								+ response.getClientResponseStatus()
										.getStatusCode() + " : "
								+ response.getClientResponseStatus());
			}
			return true;
		} else {

			this.setInTestMode(true);

			EdalConfiguration.logger
					.warn("Skiped DataCite Authentication: The publication module is running in a test mode");
			return true;
		}
	}

	/**
	 * Validate if the system if able to connect to DataCite with the given
	 * connection and proxy settings.
	 * 
	 * @return true if the validation was successful, otherwise false.
	 * 
	 * @throws EdalConfigurationException
	 *             if unable to validate the parameter.
	 */
	private boolean validateDataCiteConnection()
			throws EdalConfigurationException {
		try {
			EdalConfiguration.logger.info("Trying to connect to DataCite...");

			Authenticator.setDefault(null);

			URL url = new URL(DATACITE_MDS_URL);
			HttpURLConnection connection = (HttpURLConnection) url
					.openConnection();
			connection.setConnectTimeout(DATACITE_CONNECTION_TIMEOUT);
			connection.setReadTimeout(DATACITE_CONNECTION_READ_TIMEOUT);

			connection.setRequestProperty("Authorization", null);

			connection.getResponseCode();

			EdalConfiguration.logger
					.info("DataCite Connection Test: successful");
			return true;
		} catch (IOException e) {
			throw new EdalConfigurationException("unable to access DataCite: "
					+ e.getMessage() + ", please check your proxy settings", e);
		}
	}

	/**
	 * Validate if the Solr DataCite server is available.
	 * 
	 * @throws EdalConfigurationException
	 *             if unable to connect to Solr server.
	 */
	private boolean validateDateCiteSolrSearch()
			throws EdalConfigurationException {

		BasicHttpParams httpParams = new BasicHttpParams();

		HttpConnectionParams.setConnectionTimeout(httpParams,
				DATACITE_CONNECTION_TIMEOUT);
		HttpConnectionParams.setSoTimeout(httpParams,
				DATACITE_CONNECTION_TIMEOUT);

		DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);

		try {
			HttpHost httpProxy = new HttpHost(this.getHttpProxyHost(),
					this.getHttpProxyPort());

			httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
					httpProxy);
		} catch (EdalConfigurationException e) {
			EdalConfiguration.logger
					.debug("DataCite SolrSearch Test : no proxy settings set : "
							+ e.getMessage());
		}

		HttpSolrServer server = new HttpSolrServer(DATACITE_SEARCH_URL,
				httpClient);

		ModifiableSolrParams solrParams = new ModifiableSolrParams();
		solrParams.set(CommonParams.Q, "wind");

		/**
		 * important parameter to remove the "/select" path at the beginning:
		 * http://search.datacite.org/api?q=10.5447 and IPK and 2012&fl=doi
		 */
		solrParams.set(CommonParams.QT, "/");

		try {

			if (server.query(solrParams).getResults().isEmpty()) {
				throw new EdalConfigurationException(
						"unable to find results for query");
			}
			EdalConfiguration.logger
					.info("DataCite SolrSearch Test : successful");
		} catch (SolrServerException e) {
			EdalConfiguration.logger.error(e);
			throw new EdalConfigurationException("unable to query DOIs: "
					+ e.getMessage() + ", please check your proxy settings");
		}
		return true;
	}

	/**
	 * Validate the given proxy settings. If the user do not define any
	 * settings, the function try to read out the settings from the system.
	 * 
	 * 
	 * @return true if the validation was successful, otherwise false.
	 * @throws EdalConfigurationException
	 *             if unable to validate the parameter.
	 */
	private boolean validateProxies() throws EdalConfigurationException {

		if ((this.httpProxyHost == null || this.httpProxyHost.isEmpty())
				&& (this.httpsProxyHost == null || this.httpsProxyHost
						.isEmpty()) && (this.httpProxyPort == 0)
				& (this.httpsProxyPort == 0)) {

			EdalConfiguration.logger.info("No proxy settings set!");

			this.setUseSystemProxies(false);
		}

		if (this.isUseSystemProxies()) {

			System.setProperty("java.net.useSystemProxies", "true");

			try {
				this.getHttpProxyHost();
				System.setProperty("http.proxyHost", this.getHttpProxyHost());

			} catch (EdalConfigurationException e) {
				EdalConfiguration.logger.error("Unable not set Proxy : "
						+ e.getMessage());
			}

			try {
				this.getHttpProxyPort();
				System.setProperty("http.proxyPort",
						String.valueOf(this.getHttpProxyPort()));
			} catch (EdalConfigurationException e) {
				EdalConfiguration.logger.error("Unable not set Proxy : "
						+ e.getMessage());
			}

			try {
				this.getHttpsProxyHost();
				System.setProperty("https.proxyHost", this.getHttpsProxyHost());
			} catch (EdalConfigurationException e) {
				EdalConfiguration.logger.error("Unable not set Proxy : "
						+ e.getMessage());
			}
			try {
				this.getHttpsProxyPort();

				System.setProperty("https.proxyPort",
						String.valueOf(this.getHttpsProxyPort()));

			} catch (final EdalConfigurationException e) {
				EdalConfiguration.logger.error("Unable not set Proxy : "
						+ e.getMessage());
			}
			EdalConfiguration.logger.info("Used manual proxy settings");
		}

		else {

			System.clearProperty("http.proxyHost");
			System.clearProperty("http.proxyPort");
			System.clearProperty("https.proxyHost");
			System.clearProperty("https.proxyPort");

			ProxySearch proxySearch = new ProxySearch();

			if (PlatformUtil.getCurrentPlattform() == Platform.WIN) {
				// proxySearch.addStrategy(Strategy.IE);
				// proxySearch.addStrategy(Strategy.FIREFOX);
				proxySearch.addStrategy(Strategy.BROWSER);
				proxySearch.addStrategy(Strategy.JAVA);
				proxySearch.addStrategy(Strategy.ENV_VAR);
				// proxySearch.addStrategy(Strategy.OS_DEFAULT);
			} else if (PlatformUtil.getCurrentPlattform() == Platform.LINUX) {
				proxySearch.addStrategy(Strategy.GNOME);
				proxySearch.addStrategy(Strategy.KDE);
				proxySearch.addStrategy(Strategy.FIREFOX);
				proxySearch.addStrategy(Strategy.BROWSER);
				proxySearch.addStrategy(Strategy.JAVA);
				proxySearch.addStrategy(Strategy.ENV_VAR);
				proxySearch.addStrategy(Strategy.OS_DEFAULT);
			} else {
				proxySearch.addStrategy(Strategy.OS_DEFAULT);
			}

			/**
			 * Note: do not override the default ProxSelector
			 * ProxySelector.setDefault(proxySearch.getProxySelector());
			 * 
			 * If the ProxySelector return null, all SearchStrategies failed
			 */

			boolean findProxySettings = false;

			try {
				List<Proxy> proxyList = proxySearch.getProxySelector().select(
						new URI(DATACITE_MDS_URL));
				if (proxyList != null) {
					for (Iterator<Proxy> i = proxyList.iterator(); i.hasNext();) {
						Proxy proxy = (Proxy) i.next();
						if (proxy.type().equals(Type.HTTP)) {
							InetSocketAddress address = (InetSocketAddress) proxy
									.address();

							if (address != null) {

								System.setProperty("http.proxyHost",
										address.getHostName());
								System.setProperty("https.proxyHost",
										address.getHostName());
								System.setProperty("http.proxyPort",
										Integer.toString(address.getPort()));
								System.setProperty("https.proxyPort",
										Integer.toString(address.getPort()));
								EdalConfiguration.logger
										.info("Found HTTP Proxy : "
												+ address.getHostName() + ":"
												+ address.getPort());
								/**
								 * set the found proxy settings to the
								 * configuration for later usage
								 */
								this.setHttpProxyHost(address.getHostName());
								this.setHttpProxyPort(address.getPort());
								this.setHttpsProxyHost(address.getHostName());
								this.setHttpsProxyPort(address.getPort());
								findProxySettings = true;
							}
						}
					}
				}
			} catch (URISyntaxException | NullPointerException e) {
				EdalConfiguration.logger
						.info("Find no proxy settings automatically");
				findProxySettings = false;
			}
			if (findProxySettings) {
				EdalConfiguration.logger.info("Used automatic proxy settings");
			} else {
				EdalConfiguration.logger.info("Used empty proxy settings");
			}
		}
		return true;
	}

	/**
	 * Validate the given SMTP settings. If the user do not define any
	 * parameter, the function try to find out the parameter from the system.
	 * 
	 * @return true if the validation was successful, otherwise false.
	 * @throws EdalConfigurationException
	 *             if unable to validate the parameter.
	 */
	private boolean validateSmtpSettings() throws EdalConfigurationException {

		if (!this.getMailSmtpHost().isEmpty()) {

			Properties properties = new Properties();
			properties.put("mail.smtp.host", this.getMailSmtpHost());
			properties.put("mail.smtp.connectiontimeout",
					SMTP_CONNECTION_TIMEOUT);
			properties.put("mail.smtp.timeout", SMTP_CONNECTION_TIMEOUT);
			// properties.put("mail.smtp.starttls.enable", "true");
			properties.put("mail.smtp.ssl.trust", this.getMailSmtpHost());

			Session session = Session.getDefaultInstance(properties);

			if (this.getMailSmtpLogin().isEmpty()) {

				try {
					session.getTransport("smtp").connect(
							this.getMailSmtpHost(), null, null);

					EdalConfiguration.logger
							.info("SMTP Connection Test -> successful");
				} catch (MessagingException e) {
					EdalConfiguration.logger
							.warn("SMTP Connection Test -> failed : "
									+ e.getMessage());
					throw new EdalConfigurationException(
							"unable to connect to eMail Server, check SMTP settings : "
									+ e.getMessage());
				}
				return true;
			} else {
				try {
					session.getTransport("smtp").connect(
							this.getMailSmtpHost(), this.getMailSmtpLogin(),
							this.getMailSmtpPassword());

					EdalConfiguration.logger
							.info("SMTP Connection Test -> successful");
				} catch (MessagingException e) {
					EdalConfiguration.logger
							.warn("SMTP Connection Test -> failed : "
									+ e.getMessage());
					throw new EdalConfigurationException(
							"unable to connect to eMail Server, check SMTP settings : "
									+ e.getMessage());
				}
				return true;
			}
		} else {

			for (String mailServerNames : MAIL_SERVER_NAMES) {
				Properties properties = new Properties();
				properties.put("mail.smtp.host", mailServerNames);
				properties.put("mail.smtp.connectiontimeout",
						SMTP_CONNECTION_TIMEOUT);
				properties.put("mail.smtp.timeout", SMTP_CONNECTION_TIMEOUT);
				// properties.put("mail.smtp.starttls.enable", "true");
				properties.put("mail.smtp.ssl.trust", mailServerNames);

				Session session = Session.getDefaultInstance(properties);

				try {
					if (!this.getMailSmtpLogin().isEmpty()
							&& !this.getMailSmtpPassword().isEmpty()) {
						session.getTransport("smtp").connect(
								this.getMailSmtpLogin(),
								this.getMailSmtpPassword());

					} else {
						session.getTransport("smtp").connect();
					}
					Logger edalLogger = Logger
							.getLogger("EDAL_SMTP_ERROR_APPENDER");

					SMTPAppender smtp = new SMTPAppender();

					smtp.setName("SMTP-APP");
					smtp.setFrom(this.getEdalEmailAddress());
					smtp.setTo(getErrorEmailAddress().getAddress());
					smtp.setSubject("[eDAL ERROR]");
					smtp.setSMTPHost(mailServerNames);
					smtp.setSMTPUsername(this.getMailSmtpLogin());
					smtp.setSMTPPassword(this.getMailSmtpPassword());
					smtp.setThreshold(Level.FATAL);
					smtp.setBufferSize(512);
					smtp.setLayout(new SimpleLayout());
					smtp.activateOptions();

					edalLogger.addAppender(smtp);
					this.setErrorLogger(edalLogger);

					EdalConfiguration.logger.info("SMTP Connection Test: "
							+ mailServerNames + " : successful");
					return true;
				} catch (MessagingException e) {
					EdalConfiguration.logger.warn("SMTP Connection Test: "
							+ mailServerNames + " -> failed : "
							+ e.getMessage());
				}
			}
			throw new EdalConfigurationException(
					"unable to connect to eMail Server, check SMTP settings");
		}
	}

	/**
	 * @return the keystorePasswordForHttpListener
	 */
	protected String getKeystorePasswordForHttpListener() {
		return this.keystorePasswordForHttpListener;
	}

	/**
	 * @param keystorePasswordForHttpListener
	 *            the keystorePasswordForHttpListener to set
	 */
	protected void setKeystorePasswordForHttpListener(
			String keystorePasswordForHttpListener) {
		this.keystorePasswordForHttpListener = keystorePasswordForHttpListener;
	}

	/**
	 * @return the certificatePathForHttpListener
	 */
	protected URL getCertificatePathForHttpListener() {
		return certificatePathForHttpListener;
	}

	/**
	 * @param certificatePathForHttpListener
	 *            the certificatePathForHttpListener to set
	 */
	protected void setCertificatePathForHttpListener(
			URL certificatePathForHttpListener) {
		this.certificatePathForHttpListener = certificatePathForHttpListener;
	}
}